package tutor;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;

import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

public class ShaderProgram {
	// ProgramID
	int programID;

	// Vertex Shader ID
	int vertexShaderID;
	// Fragment Shader ID
	int fragmentShaderID;

	/**
	 * Create a new ShaderProgram.
	 */
	public ShaderProgram() {
		programID = glCreateProgram();
	}

	public void attachVertexShader(String name) {
		// Load the source
		String vertexShaderSource = FileUtil.readFromFile(name);

		// Create the shader and set the source
		vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertexShaderID, vertexShaderSource);

		// Compile the shader
		glCompileShader(vertexShaderID);

		// Check for errors
		if (glGetShaderi(vertexShaderID, GL_COMPILE_STATUS) == GL_FALSE) {
			System.err.println("Unable to create vertex shader:");
			dispose();
			Game.end();
		}

		// Attach the shader
		glAttachShader(programID, vertexShaderID);
	}

	public void attachFragmentShader(String name) {
		// Load the source
		String fragmentShaderSource = FileUtil.readFromFile(name);

		// Create the shader and set the source
		fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragmentShaderID, fragmentShaderSource);

		// Compile the shader
		glCompileShader(fragmentShaderID);

		// Check for errors
		if (glGetShaderi(fragmentShaderID, GL_COMPILE_STATUS) == GL_FALSE) {
			System.err.println("Unable to create fragment shader:");
			dispose();
			Game.end();
		}

		// Attach the shader
		glAttachShader(programID, fragmentShaderID);
	}

	public void link() {
		// Link this program
		glLinkProgram(programID);

		// Check for linking errors
		if (glGetProgrami(programID, GL_LINK_STATUS) == GL_FALSE) {
			System.err.println("Unable to link shader program:");
			dispose();
			Game.end();
		}
	}

	/**
	 * Bind this program to use.
	 */
	public void bind() {
		glUseProgram(programID);
	}

	/**
	 * Unbind the shader program.
	 */
	public static void unbind() {
		glUseProgram(0);
	}

	public void dispose() {
		// Unbind the program
		unbind();

		// Detach the shaders
		glDetachShader(programID, vertexShaderID);
		glDetachShader(programID, fragmentShaderID);

		// Delete the shaders
		glDeleteShader(vertexShaderID);
		glDeleteShader(fragmentShaderID);

		// Delete the program
		glDeleteProgram(programID);
	}

	public void setUniform(String name, Matrix4f value) {
		glUniformMatrix4(glGetUniformLocation(programID, name), false, MatrixUtil.toFloatBuffer(value));
	}

	public void setUniform(String name, Vector3f value) {
		glUniform3f(glGetUniformLocation(programID, name), value.x, value.y, value.z);
	}

	public void setUniform(String name, float value) {
		glUniform1f(glGetUniformLocation(programID, name), value);
	}

}
